﻿//-----------------------------------------------------------------------------
//<filedescription file="WpfSummaryBox.xaml.cs" company="Microsoft">
//  <copyright>
//     Copyright (c) 2008 Microsoft Corporation.  All rights reserved.
//  </copyright>
//  <purpose>
//    A summary box to display a summary of the actions a wizard has or will perform.
//  </purpose>
//  <notes>
//  </notes>
//</filedescription>
//-----------------------------------------------------------------------------
        
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Forms.VisualStyles;
using DevelopStudio.Basic;

namespace DevelopStudio.Control.Basic
{
    /// <summary>
    /// Interaction logic for SummaryBox.xaml
    /// </summary>
    internal partial class WpfSummaryBox : FlowDocumentScrollViewer
    {
        public WpfSummaryBox()
        {
            InitializeComponent();

            Clear();
        }

        public void SetSummary(IEnumerable<SmsSummaryItem> summary)
        {
            Clear();

            foreach (SmsSummaryItem summaryItem in summary)
            {
                AddItem(summaryItem);
            }
        }

        private void Clear()
        {
            Document = new FlowDocument();
            Document.FontFamily = FontFamily;
            Document.FontSize = FontSize;
            Document.FontStretch = FontStretch;
            Document.FontStyle = FontStyle;
            Document.FontWeight = FontWeight;
            Document.TextAlignment = TextAlignment.Left;
            Document.Background = System.Windows.Media.Brushes.White;
            Document.Foreground = System.Windows.Media.Brushes.Black;
        }

        private void AddItem(SmsSummaryItem summaryItem)
        {
            // Is this an Action Group?
            SmsSummaryActionGroup summaryGroup = summaryItem as SmsSummaryActionGroup;
            if (summaryGroup != null)
            {
                AddHeadline(summaryGroup.Title);

                foreach (SmsSummaryItem childSummaryItem in summaryGroup)
                {
                    AddItem(childSummaryItem);
                }
                return;
            }

            // Is it an individual Action?
            SmsSummaryAction summaryAction = summaryItem as SmsSummaryAction;
            if (summaryAction != null)
            {
                AddAction(summaryAction.Status, summaryAction.Title, summaryAction.Details, summaryAction.Warnings, summaryAction.Errors, summaryAction.CorrectiveSteps);
                return;
            }

            // Don't know what this is, just add it as a line of text.
            AddTextLine(summaryItem.Title);
        }

        private void AddHeadline(string title)
        {
            Paragraph paragraph = new Paragraph();
            if (Document.Blocks.Count == 0)
            {
                paragraph.Margin = new Thickness(0);
            }
            else
            {
                paragraph.Margin = new Thickness(0, this.FontSize, 0, 0);
            }
            paragraph.Inlines.Add(new Bold(new Underline(new Run(title))));
            Document.Blocks.Add(paragraph);
        }

        private void AddAction(SmsSummaryAction.ActionStatus actionStatus, string title,
            ICollection<string> details, ICollection<string> warnings, ICollection<string> errors, ICollection<string> correctiveSteps)
        {
            Paragraph paragraph = new Paragraph();
            paragraph.Margin = new Thickness(0);

            // Get the icon and convert it to a BitmapImage.
            Icon icon;
            string statusMessageFormat;
            switch (actionStatus)
            {
                case SmsSummaryAction.ActionStatus.Incomplete:
                    icon = Properties.Resources.Process;
                    statusMessageFormat = Properties.Resources.ActionStateIncompleteFormat;
                    break;
                case SmsSummaryAction.ActionStatus.CompleteWithSuccess:
                    icon = Properties.Resources.ok;
                    statusMessageFormat = Properties.Resources.ActionStateSuccessFormat;
                    break;
                case SmsSummaryAction.ActionStatus.CompleteWithWarnings:
                    icon = Properties.Resources.warning;
                    statusMessageFormat = Properties.Resources.ActionStateWarningFormat;
                    break;
                case SmsSummaryAction.ActionStatus.CompleteWithErrors:
                    icon = Properties.Resources.critical;
                    statusMessageFormat = Properties.Resources.ActionStateFailureFormat;
                    break;
                default: // case SmsSummaryAction.ActionStatus.NotStarted:
                    icon = null;
                    statusMessageFormat = Properties.Resources.ActionStateNotStartedFormat;
                    break;
            }
            if (icon != null)
            {
                BitmapImage bitmap = IconToBitmapImage(icon, 16, 16);

                // Put the bitmap into an Image control we can put on the screen, and add it.
                System.Windows.Controls.Image image = new System.Windows.Controls.Image();
                image.Source = bitmap;
                image.Height = 16;
                image.Width = 16;
                paragraph.Inlines.Add(image);
            }

            // Add the title. (With text for status for 508 compliance)
            paragraph.Inlines.Add(new Run(" " + string.Format(CultureInfo.InvariantCulture, statusMessageFormat, title)));
            Document.Blocks.Add(paragraph);

            // Add details, if there are any.
            if (details != null &&
                details.Count > 0)
            {
                List list = new List();
                list.Margin = new Thickness(0);
                foreach (string detail in details)
                {
                    list.ListItems.Add(FormatActionDetailString(detail));
                }
                Document.Blocks.Add(list);
            }

            // Add warnings, if there are any.
            if (warnings != null &&
                warnings.Count > 0)
            {
                paragraph = new Paragraph();
                paragraph.Margin = new Thickness(0);
                paragraph.Inlines.Add(new Bold(new Run(Properties.Resources.ActionHeaderWarnings)));
                Document.Blocks.Add(paragraph);
                
                List list = new List();
                list.Margin = new Thickness(0);
                list.MarkerStyle = TextMarkerStyle.None;
                foreach (string warning in warnings)
                {
                    list.ListItems.Add(new ListItem(new Paragraph(new Run(warning))));
                }
                Document.Blocks.Add(list);
            }

            // Add Error messages, if there are any.
            if (errors != null &&
                errors.Count > 0)
            {
                paragraph = new Paragraph();
                paragraph.Margin = new Thickness(0);
                paragraph.Inlines.Add(new Bold(new Run(Properties.Resources.ActionHeaderErrors)));
                Document.Blocks.Add(paragraph);

                List list = new List();
                list.Margin = new Thickness(0);
                list.MarkerStyle = TextMarkerStyle.None;
                foreach (string error in errors)
                {
                    list.ListItems.Add(new ListItem(new Paragraph(new Run(error))));
                }
                Document.Blocks.Add(list);
            }

            // Add Corrective steps, if there are any.
            if (correctiveSteps != null &&
                correctiveSteps.Count > 0)
            {
                paragraph = new Paragraph();
                paragraph.Margin = new Thickness(0);
                paragraph.Inlines.Add(new Bold(new Run(Properties.Resources.ActionHeaderCorrectiveSteps)));
                Document.Blocks.Add(paragraph);

                List list = new List();
                list.Margin = new Thickness(0);
                list.MarkerStyle = TextMarkerStyle.None;
                foreach (string correctiveStep in correctiveSteps)
                {
                    list.ListItems.Add(new ListItem(new Paragraph(new Run(correctiveStep))));
                }
                Document.Blocks.Add(list);
            }
        }

        private void AddTextLine(string title)
        {
            Paragraph paragraph = new Paragraph();
            paragraph.Margin = new Thickness(0);
            paragraph.Inlines.Add(new Run(title));
            Document.Blocks.Add(paragraph);
        }

        /// <summary>
        /// Format provided Details string to allow sub-Details.
        /// </summary>
        /// <param name="detail">The text for the Details to be shown on the screen.</param>
        /// <returns>A ListItem corresponding to the the Detail specified.</returns>
        /// <remarks>
        ///     Details may use "\r\n" to indicate line breaks in the details.
        ///     Lines with one or more '\t' chars on the front indicate sub-lists and will be shown in a bulleted list.
        ///     The number of '\t' chars indicates the depth of the item.
        ///     NOTE: a given line may not have more '\t' chars than the line above it, plus 1.
        /// </remarks>
        private ListItem FormatActionDetailString(string detail)
        {
            ListItem listItem = new ListItem();

            // Parse the string, then add the details.
            DetailNode rootNode = ParseActionDetailString(detail);
            if (rootNode.Children != null)
            {
                foreach (DetailNode detailNode in rootNode.Children)
                {
                    // TODO: This needs to build a List for each child node.
                    listItem.Blocks.Add(new Paragraph(new Run(detailNode.Detail)));
                    if (detailNode.Children != null)
                    {
                        listItem.Blocks.Add(GetChildList(detailNode));
                    }
                }
            }

            return listItem;
        }

        private DetailNode ParseActionDetailString(string detail)
        {
            DetailNode rootNode = new DetailNode();
            DetailNode currentNode = null;
            foreach (string detailPart in detail.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
            {
                // First time through, just add the detail.
                if (currentNode == null)
                {
                    currentNode = rootNode.AddChild(detailPart);
                    continue;
                }

                // Determine how many tabs are at the beginning of this detail
                int detailLevel = 0;
                while (detailPart[detailLevel] == '\t')
                {
                    detailLevel++;
                }
            
                if (detailLevel == currentNode.Level)
                {
                    // This one is at the same level, Add as a sibling.
                    currentNode = currentNode.Parent.AddChild(detailPart);
                }
                else if (detailLevel > currentNode.Level)
                {
                    // This one is lower-level. Add as a child.
                    currentNode = currentNode.AddChild(detailPart);
                }
                else
                {
                    // This one is higher-level. Find appropriate parent and add.
                    while (currentNode.Level != detailLevel)
                    {
                        currentNode = currentNode.Parent;
                    }
                    currentNode = currentNode.Parent.AddChild(detailPart);
                }
            }

            return rootNode;
        }

        private List GetChildList(DetailNode detailNode)
        {
            List list = new List();
            list.Margin = new Thickness(0);
            foreach (DetailNode childDetail in detailNode.Children)
            {
                ListItem listItem = new ListItem();
                listItem.Blocks.Add(new Paragraph(new Run(childDetail.Detail)));

                if (childDetail.Children != null)
                {
                    listItem.Blocks.Add(GetChildList(childDetail));
                }

                list.ListItems.Add(listItem);
            }

            return list;
        }

        /// <summary>
        /// Converts an System.Drawing.Icon object to a System.Windows.Media.Imaging.BitmapImage object for use with WPF controls.
        /// </summary>
        /// <param name="icon">The Icon to convert</param>
        /// <returns>The BitmapImage created from the Icon.</returns>
        public BitmapImage IconToBitmapImage(Icon icon)
        {
            return IconToBitmapImage(icon, 0, 0);
        }

        /// <summary>
        /// Converts an System.Drawing.Icon object to a System.Windows.Media.Imaging.BitmapImage object for use with WPF controls.
        /// </summary>
        /// <param name="icon">The Icon to convert</param>
        /// <param name="width">Width of icon desired</param>
        /// <param name="height">Height of icon desired</param>
        /// <returns>The BitmapImage created from the Icon.</returns>
        public BitmapImage IconToBitmapImage(Icon icon, int width, int height)
        {
            // Get the specified version of the icon, and convert it to a stream.
            MemoryStream stream = new MemoryStream();
            if (width == 0 || height == 0)
            {
                icon.ToBitmap().Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            }
            else
            {
                new Icon(icon, width, height).ToBitmap().Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            }
            stream.Seek(0, SeekOrigin.Begin);

            // Write the stream into a bitmap image.
            BitmapImage bitmap = new BitmapImage();
            bitmap.BeginInit();
            bitmap.StreamSource = stream;
            bitmap.EndInit();

            return bitmap;
        }

        /// <summary>
        /// Class used to hold a parsed version of a Details string.
        /// </summary>
        private class DetailNode
        {
            public string Detail { get; set;}

            public DetailNode Parent { get; set; }

            public int Level { get; internal set; }

            public List<DetailNode> Children { get; private set; }

            public DetailNode()
            {
                Level = -1;
            }

            public DetailNode AddChild(string detail)
            {
                DetailNode childNode = new DetailNode() { Parent = this, Detail = detail.Trim() };

                if (Children == null)
                {
                    Children = new List<DetailNode>();
                }

                Children.Add(childNode);
                childNode.Level = Level + 1;

                return childNode;
            }
        }
    }
}
